package com.farsight.leo.fssmartlock.net;

import com.farsight.leo.fssmartlock.Constants;
import com.farsight.leo.fssmartlock.bases.BaseApplication;
import com.farsight.leo.fssmartlock.net.bases.BaseOkHttpCallback;
import com.farsight.leo.fssmartlock.net.bases.BaseResponse;
import com.farsight.leo.fssmartlock.net.bean.AuthCodeData;
import com.farsight.leo.fssmartlock.net.bean.DeviceData;
import com.farsight.leo.fssmartlock.net.bean.UserData;
import com.farsight.leo.fssmartlock.net.response.GenAuthCodeResponse;
import com.farsight.leo.fssmartlock.net.response.GetAuthCodeListResponse;
import com.farsight.leo.fssmartlock.net.response.GetDeviceListResponse;
import com.farsight.leo.fssmartlock.net.response.RegisterDeviceResponse;
import com.farsight.leo.fssmartlock.net.response.RegisterUserResponse;
import com.farsight.leo.fssmartlock.utils.LogUtils;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class OkHttpHelper {
    private static OkHttpHelper httpHelper;
    private OkHttpClient okHttpClient;
    private static final int BUF_SIZE = 1024 * 8;

    private OkHttpHelper() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        okHttpClient = builder
                .retryOnConnectionFailure(true)
                .connectTimeout(60, TimeUnit.SECONDS)
                .build();
    }

    public static OkHttpHelper getInstance() {
        if (httpHelper == null) {
            synchronized (OkHttpHelper.class) {
                if (httpHelper == null) {
                    httpHelper = new OkHttpHelper();
                }
            }
        }
        return httpHelper;
    }

    public void requestUserRegistration(UserData user, BaseOkHttpCallback<UserData> callback) {
        postRequest(user, "/register", RegisterUserResponse.class, callback);
    }

    public void userLogin(UserData userData, BaseOkHttpCallback<UserData> callback) {
        postRequest(userData, "/login", RegisterUserResponse.class, callback);
    }

    public void bindDevice(DeviceData device, BaseOkHttpCallback<DeviceData> callback) {
        postRequest(device, "/dev/add", RegisterDeviceResponse.class, callback);
    }

    public void controlDevice(DeviceData device, BaseOkHttpCallback<DeviceData> callback) {
        postRequest(device, "/dev/remote", RegisterDeviceResponse.class, callback);
    }

    public void genAuthCode(AuthCodeData code, BaseOkHttpCallback<AuthCodeData> callback) {
        postRequest(code, "/dev/auth", GenAuthCodeResponse.class, callback);
    }

    public void getDeviceList(UserData userData, BaseOkHttpCallback<List<DeviceData>> callback) {
        postRequest(userData, "/dev/list", GetDeviceListResponse.class, callback);
    }

    public void getAuthCodeList(DeviceData device, BaseOkHttpCallback<List<AuthCodeData>> callback) {
        postRequest(device, "/auth/list", GetAuthCodeListResponse.class, callback);
    }


    private <T> void postRequest(Object jsonObj, String urlPath, final Class<? extends BaseResponse> cls,
                                 final BaseOkHttpCallback<T> callback) {
        okHttpClient.newCall(buildRequest(jsonObj, urlPath)).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                BaseApplication.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.onError(e.getMessage());
                    }
                });

            }

            @Override
            public void onResponse(Call call, final Response response) {
                String data = null;
                ResponseBody body = response.body();
                byte[] bytes = new byte[BUF_SIZE];
                if (body == null) {
                    BaseApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onError("the response body is null.");
                        }
                    });
                    return;
                }

                int read = 0;
                Arrays.fill(bytes, (byte) 0);
                int available = (int) body.contentLength();
                while (true) {
                    try {
                        read += body.byteStream().read(bytes, read, BUF_SIZE - read);
                        if (read == available) {
                            break;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        break;
                    }
                }
                data = new String(bytes, 0, read);
                LogUtils.log("available vs actual read:" + available + " vs " + read);
                LogUtils.log("final response body:" + data);

                final String finalData = data;
                BaseApplication.post(new Runnable() {
                    @Override
                    public void run() {
                        BaseResponse resp;
                        try {
                            resp = new Gson().fromJson(finalData, cls);
                        } catch (JsonSyntaxException e) {
                            callback.onError(e.getMessage());
                            return;
                        }

                        if (resp == null) {
                            callback.onError("could not decipher response due to irregularity of response string patten.");
                        } else if (resp.getCode() != 200) {
                            callback.onError(resp.getError().getMsg());
                        } else {
                            callback.onSuccess((T) resp.getData());
                        }
                    }
                });

            }
        });
    }

    private Request buildRequest(Object jsonObj, String urlPath) {
        String json = new Gson().toJson(jsonObj);
//        LogUtils.log(json);
        RequestBody requestBody = RequestBody.create(json, MediaType.parse("application/json"));
        return new Request.Builder().url(Constants.BASE_URL + urlPath).addHeader("Connection", "close").post(requestBody).build();
    }


    public void genericHttpRequest(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String jsonData="{\n" +
                            "\"user_name\":\"leo\",\n" +
                            "\"user_password\":\"123\",\n" +
                            "\"telephone_number\":\"123456783\",\n" +
                            "\"email_address\":\"leoliao@166.cm\"\n" +
                            "}\n";
                    URL url=new URL(Constants.BASE_URL+"/login");
                    HttpURLConnection connection = null;
                    try {
                        connection = (HttpURLConnection) url.openConnection();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }

                    connection.setConnectTimeout(5000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    try {
                        connection.setRequestMethod("POST");
                    } catch (ProtocolException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    connection.setUseCaches(false);
                    connection.setRequestProperty("Content-Type", "application/json");
                    connection.setInstanceFollowRedirects(true);
                    try {
                        connection.connect();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    OutputStream outputStream = null;
                    try {
                        outputStream = connection.getOutputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    try {
                        outputStream.write(jsonData.getBytes());
                        outputStream.flush();
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    byte[] data=new byte[1024*8];

                    InputStream inputStream = null;
                    try {
                        LogUtils.log(connection.getResponseCode()+"");
                        LogUtils.log(connection.getResponseMessage());
                        inputStream = connection.getInputStream();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    int read = 0;
                    try {
                        read = inputStream.read(data);
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                    String responseBody=new String(data,0,read);
                    LogUtils.log(responseBody);
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        LogUtils.log(e.getMessage());
                        return;
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                    LogUtils.log(e.getMessage());
                    return;
                }
            }
        }).start();

    }


}
